#include<iostream>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<ctime>
#include<cstdlib>
#include<vector>    
#include<sys/wait.h>
#include"task.hpp"

using namespace std;
//argc -->argument count 是命令行参数数量，
//argv -->argument vector是字符指针数组,每一个指针指向一个命令行参数字符串。
//argv[argc]是一个空字符串，标志参数列表结束。
enum {
    UsageError = 1,
    ArgError,
    PipeError
};

void Usage(const string& str)
{
    cout<<"Usage:"<<str<<"subprocess-num"<<endl;
}

class Channel
{
    public:
    Channel(int wfd,pid_t id,string& name)
    :_wfd(wfd),_sub_process_id(id),_name(name)
    {}
    ~Channel()
    {}
    //打印管道信息
    void Printbug()
    {
        cout<<"_wfd"<< _wfd;
        cout<<"_sub_process_id"<< _sub_process_id;
        cout<<"_name"<< _name <<endl;
    }
    string get_name()
    {
        return _name;
    }
    int get_wfd()
    {
        return _wfd;
    }
    pid_t get_pid()
    {
        return _sub_process_id;
    }
    void Close()
    {
        close(_wfd);
    }
    private:
    int _wfd;//管道的写文件描述符
    pid_t _sub_process_id;//子进程pid
    std::string _name;//子进程名字
};
class processpool
{
    public:
    processpool(int sub_process_num):_sub_process_num(sub_process_num)
    {}
    vector<int> fds;
    //创建进程
    int Creatprocess(work_t work)
    {
        for(int number = 0; number < _sub_process_num; number++)
        {
            //存储创建的管道的文件描述符，pipefd[0]读取端、pipefd[1]写入端
            int pipefd[2]{0};
            //pipe函数创建管道，并将管道的文件描述符写入pipefd中，创建成功返回0。
            int n = pipe(pipefd);   
            if(n<0)
            return PipeError;
            //fork创建子进程并返回子进程pid
            pid_t id = fork();
            if(id == 0)
            {
                //child->r
                close(pipefd[1]);
                //关闭继承自父进程的写文件描述符
                if(!fds.empty())
                {
                    cout<<"close fd:";
                    for(auto fd: fds)
                    {
                        cout<<fd<<" ";
                        close(fd);
                    }
                    cout<<endl;
                }
                //执行任务
                // 父进程标准输出--> 写入端  管道  读取端 <---标准输入 子进程 标准输出
                dup2(pipefd[0],0);//子进程的标准输入被重定向到管道的读取端。理解：子进程从标准输入的内容就是管道的内容
                work(pipefd[0]);
                exit(0);
            }
            string cname = "channel-"+to_string(number);
            //father-->w
            close(pipefd[0]);
            channels.push_back(Channel(pipefd[1],id,cname));//将管道管理起来(管道的写端，子进程的pid，管道号)
            fds.push_back(pipefd[1]);//记录父进程的写文件描述符
        }
        return 0;
    }
    int Nextchannel()
    {
        static int next = 0;
        int c = next;
        next++;
        next %= channels.size();
        return c;  
    }
    void SendTaskCode(int index,uint32_t code)
    {
        cout<<"send code:"<< code << " to "<<channels[index].get_name()<<" sub-process id: "<<channels[index].get_pid()<<endl;
        write(channels[index].get_wfd(),&code,sizeof(code));//向管道的写入
    }
    void Debug()
    {
        for(auto& channel : channels)
        {
            channel.Printbug();
        }
    }
    void KillAll()
    {
        for(auto& channel : channels)
        {
            channel.Close();
            std::cout << channel.get_name() << " close done" << " sub process quit now : " << channel.get_pid() << std::endl;    
            pid_t pid = channel.get_pid();
            pid_t rid = waitpid(pid,nullptr,0);
            if(rid == pid)
            {
                cout<<"wait sub process : "<<pid<<" success... "<<endl;
            }
        }
    }
    void WaitProcess()
    {
        // for(auto& channel : channels)
        // {
        //     pid_t pid = channel.get_pid();
        //     pid_t rid = waitpid(pid,nullptr,0);
        //     if(rid == pid)
        //     {
        //         cout<<"wait sub process : "<<pid<<" success... "<<endl;
        //     }

        // }
    }
    ~processpool()
    {}
    private:
    int _sub_process_num;
    vector<Channel> channels;
};


void CtrlProcess(processpool* ProcesspoolPtr,int cnt)
{
    //控制进程
    while(cnt)
    {
        //1. 选择一个进程和通道
        int channel = ProcesspoolPtr->Nextchannel();
        //2. 选择一个任务
        uint32_t code  = NextTask();
        //3. 发送任务
        ProcesspoolPtr->SendTaskCode(channel,code);
        sleep(1);
        cnt--;
   }
}
int main(int argc,char * argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return UsageError;
    }
    int sub_process_num = stoi(argv[1]);//将第二个命令行参数(字符串)转换为整数类型
    if(sub_process_num < 0)
    {
        return ArgError;//返回参数错误
    }
    srand((uint64_t)time(nullptr));
    //创建通信信道和子进程
    processpool* ProcesspoolPtr = new processpool(sub_process_num);
    ProcesspoolPtr->Creatprocess(worker);
    //控制进程
    CtrlProcess(ProcesspoolPtr,5);
    cout<<"tasks done!"<<endl;

   //回收子进程
   //1. 如何让所有子进程退出？---当管道的写端关闭时，子进程退出
   //2. 让所有已经退出的子进程回收资源
   ProcesspoolPtr->KillAll();
   //wait sub process
   ProcesspoolPtr->WaitProcess();
   delete ProcesspoolPtr;
   return 0;
}